/*
 * File:    eg_data.c
 * Purpose: Implementation of data structure related functions
 */

/*
 Copyright 2009 James Youngquist, Colin Scott.

 This file is part of ElectroGladiator.  ElectroGladiator is free
 software: you can redistribute it and/or modify it under the terms
 of the GNU General Public License as published by  the Free
 Software Foundation, either version 3 of the License, or (at your
 option) any later version.

 You should have received a copy of the GNU General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <stdio.h>
#include <math.h>

#include "eg_data.h"

//-----------------------
// Private function stubs
//-----------------------

/* Finds the point at the edge of the owner's radius 
 * where a new rock should be created
 */
Point* place_rock (Player *owner); 

//-------------
// LINKED LISTS
//-------------

/* Sets up a linked list for use. Use macros for node type (i.e. printer) */
void ll_init(LL* list, ll_printer p) {
    list->front = NULL;
    list->length = 0;
    list->printer = p;

    return;
}

/* Treats linked list as a queue and prepends data to the front.
 * Returns a pointer to the added node.
 */
Node* ll_push(LL* list, void* data) {
    Node *node = new_node(data, list->front);
    list->front = node;
    list->length++;
    return node;
}

/* Treats linked list as a queue and removes frontmost data.
 * Returns pointer to removed data or NULL if empty.
 */
void* ll_pop(LL* list) {
    Node* old_front = list->front;

    // not empty
    if (list->front) {
        list->front = list->front->next;
        list->length--;
        void* data = old_front->data;
        free_node(old_front);
        return data;
    }

    return old_front;
}

/* Returns number of elements in the linked list. */
int ll_length(const LL* list) {
    return list->length;
}

/* Pretty prints a linked list */
void ll_print(const LL* list) {
    Node *cur;
    for (cur = list->front; cur; cur = cur->next) {
        (*list->printer)(cur->data);
        printf("\n");
    }
}

/**
 * Removes the node from the list and returns the data that was stored therein.
 * @param list Linked list to remove node from
 * @param node Node to remove from list
 */
void* ll_rm_node(LL *list, Node *node) {
    if (!node) return NULL;

    if (node == list->front) list->front = list->front->next; // fencepost
    else { // fence
        Node *cur = list->front;
        while (cur->next != node)
            cur = cur->next;
        cur->next = node->next; // jump over node in list
    }

    void *data = node->data; // temp variable to save data location
    free_node(node); // free unneeded node
    list->length--;
    return data;
}

/*
 * Frees memory used by a linked list.
 * Pre: All nodes and node data were allocated on the heap and can be free()'d
 */
void ll_clear(LL* list) {
    Node *current = list->front;
    Node *next = current;

    while (current) {
        next = current->next;
        eradicate_node(current);
        current = next;
    }

    list->length = 0;
    list->front = NULL;

    return;
}

/*
 * Returns a pointer to the data stored at
 * the given index. Prints an error message
 * and returns NULL if the index is incorrect.
 */
void* ll_get(LL* list, int index) {
	if (index < 0 || index > list->length - 1) {
		fprintf(stderr, "Index out of bounds for LL\n");
		return NULL;
	}
	
	int i;
	Node *current = list->front;
	for (i = 0; i < index; i++) {
		current = current->next;
	}
	return current->data;
}

/* 
 * Iterator for linked lists Part I. This function is called
 * first and returns a pointer to the first node in the LL,
 * or NULL if the list is empty or the LL is NULL to begin with
 */
Node* ll_itr_first (const LL* list) {
	if (list)   // robust code > preconditions
		return list->front;
	else
		return NULL;
}

/*
 * Iterator for linked lists Part 2. This function is the
 * normal itr.next function in OOP.
 */
void* ll_itr_next (const Node* curr) {
	if (curr)	// robust code > preconditions
		return curr->next;
	else 
		return NULL;
}

// CONSTRUCTORS and DESTRUCTORS
// Note the DESTRUCTORS aren't needed right now, but I put them in before
// realizing that and might as well leave them there in case...

/* Creates a new Node on the heap initialized with the arguments */
Node* new_node(void *data, Node *next) {
    Node* node = calloc(1, sizeof(Node));
    node->data = data;
    node->next = next;
    return node;
}

/* Creates a new Point on the heap initialized with the arguments */
Point* new_point(double x, double y) {
    Point *p = calloc(1, sizeof(Point));
    p->x = x;
    p->y = y;
    return p;
}

/* Creates a new Line on the heap initialized with the arguments */
Line* new_line(double x1, double y1, double x2, double y2) {
    Line *line = calloc(1, sizeof(Line));
    line->p1.x = x1;
    line->p1.y = y1;
    line->p2.x = x2;
    line->p2.y = y2;
    return line;
}

// Creates a ray of specified length and starting point, allocated on the heap
Line* make_ray(const Point *start, double angle, double length) {
    double x = length * cos(angle) + start->x;
    double y = length * sin(angle) + start->y;

    return new_line(start->x, start->y, x, y);
}

/* Creates a new Trigon on the heap initialized with the arguments */
Trigon* new_trigon(Point p1, Point p2, Point p3) {
    Trigon *t = calloc(1, sizeof(Trigon));
    t->p1 = p1;
    t->p2 = p2;
    t->p3 = p3;
    return t;
}

/* Creates a new Player on the heap initialized with the arguments */
Player* new_player(double x, double y, double bearing) {
    Player *p = calloc(1, sizeof(Player));
    p->location.x = x;
    p->location.y = y;
    p->bearing = bearing;
    ll_init(&p->visible_lines, LINE);
    ll_init(&p->vision_triangles, TRIGON);
    ll_init(&p->visible_players, PLAYER);
    ll_init(&p->visible_rocks, ROCK);
    p->PID = next_PID();

    return p;
}

/* Creates a new Rock on the heap */
Rock* new_rock(double x, double y, double bearing, Player *owner) {
	Rock *r = calloc(1, sizeof(Rock));
	r->location.x = x;
	r->location.y = y;
	r->bearing = bearing;
	r->speed = R_SPEED;
	r->owner = owner;
	
	return r;
}

//=== FREEING ===//

/* Frees memory used by a Node, but not the data it contains. */
void free_node(Node *n) {
    if (n) {
        free(n);
    } else {
        fprintf(stderr, "Tried to free a NULL node\n");
    }
    return;
}

/* Frees ALL memory used by a Node including the data it contains. */
void eradicate_node(Node *n) {
    if (n) {
        free(n->data);
        free(n);
    } else {
        fprintf(stderr, "Tried to free a NULL node\n");
    }
    return;
}

/* Frees memory used by a Point. */
void free_point(Point *p) {
    if (p) {
        free(p);
    } else {
        fprintf(stderr, "Tried to free NULL Point\n");
    }
    return;
}

/* Frees memory used by a Line */
void free_line(Line *l) {
    if (l) {
        free(l);
    } else {
        fprintf(stderr, "Tried to free NULL Line\n");
    }
    return;
}

/* Frees memory used by a Trigon */
void free_trigon(Trigon *t) {
    if (t) {
        free(t);
    } else {
        fprintf(stderr, "Tried to free NULL Trigon\n");
    }
    return;
}

/* Frees memory used by a Player */
void free_player(Player *p) {
	if (p) {
		free(p);
	} else {
		fprintf(stderr, "Tried to free NULL Player\n");
	}
	return;
}

/* Frees memory used by a Rock */
void free_rock(Rock *r) {
	if (r) {
		free(r);
	} else {
		fprintf(stderr, "Tried to free NULL Rock\n");
	}
	return;
}

//=== PRINTING ===//

/* displays (x, y) */
void print_point(void* p) {
    Point *point = (Point *) p;
    printf("(%6.3f, %6.3f)", point->x, point->y);
    return;
}

/* displays [(x1, y1)--(x2, y2)] */
void print_line(void* l) {
    Line *line = (Line *) l;
    printf("[");
    print_point(&line->p1);
    printf("--");
    print_point(&line->p2);
    printf("]");
    return;
}

/* displays <(x1, y1)^^(x2, y2)^^(x3, y3)> */
void print_trigon(void* t) {
    Trigon *tri = (Trigon*) t;
    printf("<");
    print_point(&tri->p1);
    printf("^^");
    print_point(&tri->p2);
    printf("^^");
    print_point(&tri->p3);
    printf(">");
    return;
}

/* displays {(x, y) @ bearing} */
void print_player(void* p) {
    Player *player = (Player*) p;
    printf("{\n");
    print_point(&player->location);
    printf(" @ %6.3f\n", player->bearing);
    ll_print(&player->visible_lines);
    ll_print(&player->vision_triangles);
    printf("}");
}

/** Displays ! A rock thrown by PID is at (x, y) with bearing N.NNN */
void print_rock(void* r) {
    Rock *rock = (Rock*) r;
    printf("! A rock thrown by PID %d is at ", rock->owner->PID);
    print_point(&rock->location);
    printf(" with bearing %3.f", rock->bearing);
}

//=== OTHER ===//

/**
 * @return Returns next PID number
 */
int next_PID (void) {
    int next_player_id = 0;
    return next_player_id++;
}

// Usable AlmostEqual function
int fnear(double A, double B) {

    return fabs(A - B) < 1e-9;

    // From
    // http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
    // and modified for doubles and longs

    //    long int aInt = *(long int *)&A;
    //    // Make aInt lexicographically ordered as a twos-complement int
    //    if (aInt < 0)
    //        aInt = 0x8000000000000000 - aInt;
    //    // Make bInt lexicographically ordered as a twos-complement int
    //    long int bInt = *(long int *)&B;
    //    if (bInt < 0)
    //        bInt = 0x8000000000000000 - bInt;
    //    //long int intDiff = labs(aInt - bInt); // labs not working
    //    long int intDiff = labs(aInt - bInt);
    //
    //    if (intDiff <= maxUlps)
    //        return 1;
    //    else
    //        return 0;
}

//=== Private Functions ===/

/* Returns a point representing where
 * a rock should be placed when constructed.
 * This is at the edge of the player's radius.
 */
Point* place_rock (Player *owner) {
	Point *p = calloc(1, sizeof(Point));
	
	// Take owner's bearing, and travel along the line made
	// by the bearing P_RAD units.	
	p->x = owner->location.x + P_RAD * cos(owner->bearing);

  p->y = owner->location.y + P_RAD * sin(owner->bearing);

	return p;
}

